﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Diagnostics;

namespace CodeDomHelper.Types
{

    [DebuggerDisplay("{AssemblyQualifiedNameUltraShort}", Name = "TypeExpression")]
    [Serializable()]
    public class BuildTypeExpression : IEnumerable<TypeExpression>
    {

        #region private
        
        private const string patternShort = @",?\sVersion=\d+\.\d+\.\d+\.\d+,?|\s,?Culture=\w+,?|\s,?PublicKeyToken=[\w\d]+,?";
        private const string patternUltraShort = @",?\sVersion=\d+\.\d+\.\d+\.\d+,?|\s,?Culture=\w+,?|\s,?PublicKeyToken=[\w\d]+,?|, mscorlib";
        protected LexicalAnalyzer _lexicalAnalyzer;
        public List<TypeExpression> Liste;

        internal protected virtual void Parse()
        {
            _lexicalAnalyzer.MoveNext();
            ExpressionValue = GetExpression();      
        }



        #endregion

        #region ctor
        
        [System.Diagnostics.DebuggerStepThrough]
        protected BuildTypeExpression()
        {
            this.Liste = new List<TypeExpression>();
        }

        protected BuildTypeExpression _rootExpression;

        [System.Diagnostics.DebuggerStepThrough]
        public BuildTypeExpression(BuildTypeExpression rootExpression)
            : this(rootExpression._lexicalAnalyzer)
        {
            _rootExpression = rootExpression;
        }

        [System.Diagnostics.DebuggerStepThrough]
        protected BuildTypeExpression(LexicalAnalyzer lexicalAnalyzer) : this()
        {
            // TODO: Complete member initialization
            this._lexicalAnalyzer = lexicalAnalyzer;            
        }


        #endregion

        #region static
        

        public static TypeExpression Analyse(string requete)
        {
            BuildTypeExpression e = new BuildTypeExpression(new LexicalAnalyzer(requete));
            e.Parse();
            return e.ExpressionValue;
        }


        public static TypeExpression Analyse(Type type)
        {
            return Analyse(type.AssemblyQualifiedName);
        }

        #endregion


        protected TypeExpression GetExpression()
        {

            TypeExpression ret = null;

            if (_lexicalAnalyzer.IsArray)
                ret = new TypeArrayExpression(this) { Generic = _lexicalAnalyzer.Generic, TypeName = _lexicalAnalyzer.Value, IsArray = true };

            else if (_lexicalAnalyzer.Generic == 0)
                ret = new TypeExpression(this) { Generic = _lexicalAnalyzer.Generic, TypeName = _lexicalAnalyzer.Value };
            
            else
                ret = new TypeGenericExpression(this) { Generic = _lexicalAnalyzer.Generic, TypeName = _lexicalAnalyzer.Value };


            ret.Parse();

            return ret;

        }


        #region Add Generic

        public void Add(TypeExpression item)
        {
            this.Liste.Add(item);
            Generic = this.Liste.Count;
        }

        public void Add(int position, TypeExpression item)
        {
            this.Liste.Insert(position, item);
            Generic = this.Liste.Count;
        }

        public void Add(string item)
        {
            var i = BuildTypeExpression.Analyse(item);
            this.Liste.Add(i);
            Generic = this.Liste.Count;
        }

        public void Add(int position, string item)
        {
            var i = BuildTypeExpression.Analyse(item);
            this.Liste.Insert(position, i);
            Generic = this.Liste.Count;
        }

        public void Add(Type item)
        {
            var i = BuildTypeExpression.Analyse(item.AssemblyQualifiedName);
            this.Liste.Add(i);
            Generic = this.Liste.Count;
        }

        public void Add(int position, Type item)
        {
            var i = BuildTypeExpression.Analyse(item.AssemblyQualifiedName);
            this.Liste.Insert(position, i);
            Generic = this.Liste.Count;
        }

        #endregion

        #region Properties

        public TypeExpression ExpressionValue { get; set; }

        public int Generic { get; protected set; }
        public int Rank { get; protected set; }        

        public string TypeName { get; set; }

        public string AssemblyName 
        {
            get
            {
                string[] tb = TypeName.Split(',');

                if (tb.Length >= 2)
                    return tb[1].Trim();

                return string.Empty;     
            }
        }        

        public string Name 
        {
            get
            {

                StringBuilder s = new StringBuilder();

                var t = TypeName.Split(',')[0].Trim();
                int i = t.LastIndexOf('.');
                string[] tb = t.Split('.');

                s.Append(tb[tb.Length - 1]);

                if (IsArray)
                {
                    s.Append("[");
                    for (int i1 = 0; i1 < Rank - 1; i1++)
                        s.Append(",");
                    s.Append("]");
                }

                return s.ToString();

            }
        }

        public bool IsCanGeneric { get { return Generic > 0; } }
        public bool IsGeneric { get { return Liste.Count > 0; } }
        public int CountGeneric { get { return Liste.Count; } }

        public string Namespace 
        {
            get
            {
                if (string.IsNullOrEmpty(TypeName))
                    return String.Empty;

                var t = TypeName.Split(',')[0].Trim();

                int i = t.LastIndexOf('.');

                if (i != -1)                
                    t = t.Substring(0, i);
                
                return t;
            }
        }


        /// <summary>
        /// Gets the full name.
        /// </summary>
        /// <value>The full name.</value>
        public string FullName
        {
            get
            {

                StringBuilder s = new StringBuilder();

                int i = TypeName.IndexOf(',');

                if (i != -1)
                    s.Append(TypeName.Substring(0, i));
                else
                    s.Append(TypeName);

                if (IsArray)
                {
                    s.Append("[");
                    for (int i1 = 0; i1 < Rank - 1; i1++)
                        s.Append(",");
                    s.Append("]");
                }

                string separator = string.Empty;

                if (this.Liste.Count >= 1)
                {
                    s.Append("[");
                    foreach (BuildTypeExpression item in this.Liste)
                    {
                        s.Append(separator);
                        s.Append("[");
                        s.Append(item.FullName);
                        s.Append("]");
                        if (this.Liste.Count > 1)
                            separator = ",";
                    }
                    s.Append("]");
                }

                return s.ToString();


            }

        }

        public TypeExpression SubType { get { return Liste.FirstOrDefault(); } }
        

        public string AssemblyQualifiedNameShort
        {
            get
            {
                string ret = AssemblyQualifiedName;
                Regex r = new Regex(patternShort, RegexOptions.Compiled);
                ret = r.Replace(ret, string.Empty);
                return ret;
            }
        }


        public string AssemblyQualifiedNameUltraShort
        {
            get
            {

                StringBuilder s = new StringBuilder();

                int i = TypeName.IndexOf(',');

                if (i != -1)
                    s.Append(TypeName.Substring(0, i));
                else
                    s.Append(TypeName);

                if (IsArray)
                {
                    s.Append("[");
                    for (int i1 = 0; i1 < Rank - 1; i1++)
                        s.Append(",");
                    s.Append("]");
                }

                string separator = string.Empty;

                if (this.Liste.Count >= 1)
                {
                    s.Append("[");
                    foreach (BuildTypeExpression item in this.Liste)
                    {
                        s.Append(separator);                  
                        s.Append(item.AssemblyQualifiedNameUltraShort);
                        if (this.Liste.Count > 1)
                            separator = ",";
                    }
                    s.Append("]");
                }

                return s.ToString();


            }
        }

 
        public string AssemblyQualifiedName
        {
            get
            {
                StringBuilder s = new StringBuilder();

                int i = TypeName.IndexOf(',');

                if (i != -1)
                    s.Append(TypeName.Substring(0, i));
                else
                    s.Append(TypeName);

                if (IsArray)
                {
                    s.Append("[");
                    for (int i1 = 0; i1 < Rank - 1; i1++)                    
                        s.Append(",");                    
                    s.Append("]");
                }
                              
                string separator = string.Empty;

                if (this.Liste.Count >= 1)
                {
                    s.Append("[");
                    foreach (BuildTypeExpression item in this.Liste)
                    {
                        s.Append(separator);
                        s.Append("[");
                        s.Append(item.AssemblyQualifiedName);
                        s.Append("]");

                        if (this.Liste.Count > 1)
                            separator = ",";

                    }
                    s.Append("]");
                }

                if (i != -1)
                {
                    var t = TypeName.Substring(i, TypeName.Length - i);
                    s.Append(t);
                }
                return s.ToString();

            }
        }


        public BuildTypeExpression this[int index] { get { return Liste[index]; } }

        public bool IsArray { get; internal set; }

        #endregion


        public Type Evaluate(Func<string, Type> ActionResolver)
        {

            Type t = ActionResolver(TypeName);

            if (t == null)
            {

                t = Type.GetType(TypeName);

                if (t == null)
                    return null;
            }

            if (t.IsGenericType)
            {
                List<Type> a = new List<Type>();

                foreach (TypeExpression item in Liste)
                    a.Add(item.Evaluate(ActionResolver));

                t = t.MakeGenericType(a.ToArray());
            }

            return t;
        }


        public Type Evaluate()
        {
            return Evaluate((e) => FindType(e));
        }


        private Type FindType(string typeName)
        {

            if (!typeName.Contains("["))
            {

                Type t = Type.GetType(typeName);
                
                if (t != null)
                    return t;
            }
            else
            {
                var e = (TypeExpression)typeName;
                return e.Evaluate(c => FindType(c));
            }

            return null;
        }


        public override string ToString()
        {
            return AssemblyQualifiedNameUltraShort;
        }


        public string CSharp
        {
            get
            {
                if (string.IsNullOrEmpty(TypeName))
                    return String.Empty;

                string ret = TypeName.Split(',')[0];

                if (IsArray)
                {
                    ret += "[";
                    for (int i1 = 0; i1 < Rank - 1; i1++)
                        ret += ",";
                    ret += "]";
                }


                if (this.Liste != null && this.Liste.Count >= 1)
                {

                    int i1 = ret.LastIndexOf('`');

                    if (i1 != -1)
                        ret = ret.Substring(0, i1);

                    ret += "<";

                    int i = 0;
                    foreach (BuildTypeExpression item in this.Liste)
                    {
                        ret += item.FullName;

                        i++;

                        if (i < this.Liste.Count)
                            ret += ", ";
                    }

                    ret += ">";

                }
                return ret;
            }
        }


        public TypeExpression Translate(Type type) { return (TypeExpression)Translate(type.AssemblyQualifiedName); }

    
        public TypeExpression Translate(string type)
        {
            TypeExpression i = (TypeExpression)this.MemberwiseClone();
            i.TypeName = type;
            return i;
        }


        public TypeExpression Clone()
        {
            TypeExpression i = (TypeExpression)this.MemberwiseClone();
            return i;
        }

   
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.Liste.GetEnumerator();
        }

        public IEnumerator<TypeExpression> GetEnumerator()
        {
            return this.Liste.GetEnumerator();
        }

        public override bool Equals(object obj)
        {

            if (obj.GetType() == GetType())
                    return (obj as TypeExpression).AssemblyQualifiedNameUltraShort == this.AssemblyQualifiedNameUltraShort;

            string typeName = obj as string;
            
            if (obj is Type)
                typeName = (obj as Type).AssemblyQualifiedName;

            if (!string.IsNullOrEmpty(typeName))
            {
                var tt = TypeExpression.Analyse(typeName);
                return tt.AssemblyQualifiedNameUltraShort == this.AssemblyQualifiedNameUltraShort;
            }

            return false;
            
        }


        

        public override int GetHashCode()
        {
            return AssemblyQualifiedName.GetHashCode();
        }


        public static explicit operator BuildTypeExpression(Type type)
        {
            return BuildTypeExpression.Analyse(type.AssemblyQualifiedName);
        }

    
        public static explicit operator BuildTypeExpression(string type)
        {
            return BuildTypeExpression.Analyse(type);
        }


    }

}

